package zheezes.swt.databinding;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.databinding.observable.set.ISetChangeListener;
import org.eclipse.core.databinding.observable.set.SetChangeEvent;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.swt.graphics.Image;

public class TreeObservableLabelProvider extends LabelProvider {
	private final Class<?> clazz;
	private final Method getTextMethod;
	private final Method getImageMethod;
	
	public TreeObservableLabelProvider(IObservableSet allElementsObservable,
			Class<?> clazz,
			String textProperty,
			String imageProperty) {
		observableSet = allElementsObservable;
		this.clazz = clazz;
		getTextMethod = CalssUtil.getMethod(this.clazz, textProperty);
		getImageMethod = CalssUtil.getMethod(this.clazz, imageProperty);
		List<String> properties = new ArrayList<String>();
		if (getTextMethod != null) {
			properties.add(textProperty);
		}
		if (getImageMethod != null) {
			properties.add(imageProperty);
		}
		listenerSupport = new ListenerSupport(m_propertiesListener, properties);
		observableSet.addSetChangeListener(m_setListener);
	}
	
	@Override
	public String getText(Object element) {
		Object text = CalssUtil.invokeMethod(getTextMethod, clazz, element);
		return text == null ? null : text.toString();
	}
	@Override
	public Image getImage(Object element) {
		return (Image) CalssUtil.invokeMethod(getImageMethod, clazz, element);
	}
	
	private final IObservableSet observableSet;
	private final ListenerSupport listenerSupport;
	
	private final PropertyChangeListener m_propertiesListener = new PropertyChangeListener() {
		public void propertyChange(PropertyChangeEvent event) {
			LabelProviderChangedEvent newEvent =
					new LabelProviderChangedEvent(TreeObservableLabelProvider.this, event.getSource());
			fireLabelProviderChanged(newEvent);
		}
	};
	
	private final ISetChangeListener m_setListener = new ISetChangeListener() {
		public void handleSetChange(SetChangeEvent event) {
			for (Object removedElement : event.diff.getRemovals()) {
				if (CalssUtil.instanceOf(clazz, removedElement)) {
					listenerSupport.unhookListener(removedElement);
				}
			}
			for (Object addedElement : event.diff.getAdditions()) {
				if (CalssUtil.instanceOf(clazz, addedElement)) {
					listenerSupport.hookListener(addedElement);
				}
			}
		}
	};
	
	@Override
	public void dispose() {
		observableSet.removeSetChangeListener(m_setListener);
		listenerSupport.dispose();
		super.dispose();
	}
}